/*
 * restserver - A smart, efficient, low consumption RESTful application service platform writen in C
 * author	: calvin
 * email	: calvinwilliams@163.com
 *
 * Licensed under the Apache License v2.0, see the file LICENSE in base directory.
 */

#include "in.h"

struct RestServiceControlerUriPath
{
	char					*http_uri_path ;
	int					http_uri_path_len ;
} ;

struct RestServiceRoute
{
	char					http_method[ HTTP_METHOD_MAX_LEN+1 ] ;
	int					http_method_len ;
	struct RestServiceControlerUriPath	http_uri_paths[ RESTSERVER_URI_PATHS_MAX_COUNT ] ;
	int					http_uri_paths_count ;
	funcRestServiceEntry			*pfuncRestServiceEntry ;
	
	struct list_head			route_list_node ;
} ;

struct RestServiceControler
{
	struct list_head			routes_list ;
} ;

static int _ParseUriPathsMatch( struct RestServiceRoute *p_route , char *uri_paths_match )
{
	char		*p1 = NULL ;
	char		*p2 = NULL ;
	
	if( uri_paths_match[0] != '/' )
		return RESTSERVER_ERROR_URI_FIRST_CHARACTER_IN_CONFIG;
	
	p_route->http_uri_paths_count = 0 ;
	p2 = p1 = uri_paths_match + 1 ;
	while(1)
	{
		if( (*p2) == '\0' )
		{
			if( p_route->http_uri_paths_count >= sizeof(p_route->http_uri_paths)/sizeof(p_route->http_uri_paths[0]) )
				return RESTSERVER_ERROR_TOO_MANY_HTTP_URI_PATHS_IN_CONFIG;
			p_route->http_uri_paths[p_route->http_uri_paths_count].http_uri_path = p1 ;
			p_route->http_uri_paths[p_route->http_uri_paths_count].http_uri_path_len = p2 - p1 ;
			DEBUGLOG( "parse out config path[%.*s]" , p_route->http_uri_paths[p_route->http_uri_paths_count].http_uri_path_len,p_route->http_uri_paths[p_route->http_uri_paths_count].http_uri_path )
			p_route->http_uri_paths_count++;
			
			return 0;
		}
		else if( (*p2) == '/' )
		{
			if( p_route->http_uri_paths_count >= sizeof(p_route->http_uri_paths)/sizeof(p_route->http_uri_paths[0]) )
				return -1;
			p_route->http_uri_paths[p_route->http_uri_paths_count].http_uri_path = p1 ;
			p_route->http_uri_paths[p_route->http_uri_paths_count].http_uri_path_len = p2 - p1 ;
			DEBUGLOG( "parse out config path[%.*s]" , p_route->http_uri_paths[p_route->http_uri_paths_count].http_uri_path_len,p_route->http_uri_paths[p_route->http_uri_paths_count].http_uri_path )
			p_route->http_uri_paths_count++;
			
			p2 = p1 = p2 + 1 ;
		}
		else
		{
			p2++;
		}
	}
}

struct RestServiceControler *CreateRestServiceControler( struct RestServiceConfig *config_array )
{
	struct RestServiceControler	*p_controler = NULL ;
	struct RestServiceConfig	*p_config = NULL ;
	struct RestServiceRoute		*p_route = NULL ;
	
	int				nret = 0 ;
	
	DEBUGLOG( "enter RSAPICreateRestServiceControler" )
	
	p_controler = (struct RestServiceControler *)malloc( sizeof(struct RestServiceControler) ) ; 
	if( p_controler == NULL )
		return NULL;
	memset( p_controler , 0x00 , sizeof(struct RestServiceControler) );
	
	INIT_LIST_HEAD( & (p_controler->routes_list) );
	
	for( p_config = config_array ; p_config->http_method[0] ; p_config++ )
	{
		p_route = (struct RestServiceRoute *)malloc( sizeof(struct RestServiceRoute) ) ;
		if( p_route == NULL )
		{
			RSAPIDestroyRestServiceControler( p_controler );
			return NULL;
		}
		
		p_route->http_method_len = strlen(p_config->http_method) ;
		if( p_route->http_method_len > sizeof(p_route->http_method)-1 )
			p_route->http_method_len = sizeof(p_route->http_method)-1 ;
		strncpy( p_route->http_method , p_config->http_method , p_route->http_method_len );
		
		nret = _ParseUriPathsMatch( p_route , p_config->http_uri_paths_match ) ;
		if( nret )
		{
			ERRORLOG( "_ParseUriPathsMatch failed[%d] , uri_paths_match[%s]" , nret , p_config->http_uri_paths_match )
			RSAPIDestroyRestServiceControler( p_controler );
			return NULL;
		}
		
		p_route->pfuncRestServiceEntry = p_config->pfuncRestServiceEntry ;
		
		list_add_tail( & (p_route->route_list_node) , & (p_controler->routes_list) );
		INFOLOG( "add a route[%.*s][%d][%.*s%s][%p] to rest service control" , p_route->http_method_len,p_route->http_method , p_route->http_uri_paths_count , (p_route->http_uri_paths_count>0?p_route->http_uri_paths[0].http_uri_path_len:0),(p_route->http_uri_paths_count>0?p_route->http_uri_paths[0].http_uri_path:"") , (p_route->http_uri_paths_count>0?",...":"") , p_route->pfuncRestServiceEntry )
	}
	
	DEBUGLOG( "leave RSAPICreateRestServiceControler" )
	
	return p_controler;
}

int DispatchRestServiceControler( struct RestServiceControler *controler , struct RestServerContext *ctx )
{
	struct RestServiceRoute	*p_route = NULL ;
	
	char			*ctx_method = NULL ;
	int			ctx_method_len ;
	
	int			ctx_paths_count ;
	
	int			uri_path_index ;
	char			*ctx_uri_path = NULL ;
	int			ctx_uri_path_len ;
	
	int			nret = 0 ;
	
	DEBUGLOG( "enter RSAPIDispatchRestServiceControler" )
	
	list_for_each_entry( p_route , & (controler->routes_list) , struct RestServiceRoute , route_list_node )
	{
		DEBUGLOG( "travel a route[%.*s][%d][%.*s%s][%p] in rest service control" , p_route->http_method_len,p_route->http_method , p_route->http_uri_paths_count , (p_route->http_uri_paths_count>0?p_route->http_uri_paths[0].http_uri_path_len:0),(p_route->http_uri_paths_count>0?p_route->http_uri_paths[0].http_uri_path:"") , (p_route->http_uri_paths_count>0?",...":"") , p_route->pfuncRestServiceEntry )
		
		ctx_method = RSAPIGetHttpMethodPtr( ctx , & ctx_method_len ) ;
		ctx_paths_count = RSAPIGetHttpUriPathsCount(ctx) ;
		DEBUGLOG( "diff config_method[%.*s] and http_method[%.*s] , diff config_paths_count[%d] and http_paths_count[%d]" , p_route->http_method_len,p_route->http_method , ctx_method_len,ctx_method , p_route->http_uri_paths_count , ctx_paths_count )
		if( STRNCMPSTRN( p_route->http_method , p_route->http_method_len , == , ctx_method , ctx_method_len ) && p_route->http_uri_paths_count == ctx_paths_count )
		{
			uri_path_index = 0 ;
			while( uri_path_index < p_route->http_uri_paths_count )
			{
				ctx_uri_path = RSAPIGetHttpUriPathPtr( ctx , uri_path_index+1 , & ctx_uri_path_len ) ;
				DEBUGLOG( "uri_path_index[%d] : diff config_path[%.*s] and http_path[%.*s]" , uri_path_index , p_route->http_uri_paths[uri_path_index].http_uri_path_len,p_route->http_uri_paths[uri_path_index].http_uri_path , ctx_uri_path_len,ctx_uri_path )
				if( STRNCMPRSTR( p_route->http_uri_paths[uri_path_index].http_uri_path , p_route->http_uri_paths[uri_path_index].http_uri_path_len , == , "{}" ) )
					;
				else if( STRNCMPSTRN( p_route->http_uri_paths[uri_path_index].http_uri_path , p_route->http_uri_paths[uri_path_index].http_uri_path_len , == , ctx_uri_path , ctx_uri_path_len ) )
					;
				else
					break;
				uri_path_index++;
			}
			if( uri_path_index == p_route->http_uri_paths_count )
			{
				struct RestServiceRoute		*p_first_rut = list_first_entry( & (controler->routes_list) , struct RestServiceRoute , route_list_node ) ;
				
				if( p_route != p_first_rut )
				{
					list_del( & (p_route->route_list_node) );
					LIST_ADD( & (p_route->route_list_node) , & (controler->routes_list) );
				}
				
				DEBUGLOG( "call RestService in route[%.*s][%d][%.*s%s][%p] in rest service control" , p_route->http_method_len,p_route->http_method , p_route->http_uri_paths_count , (p_route->http_uri_paths_count>0?p_route->http_uri_paths[0].http_uri_path_len:0),(p_route->http_uri_paths_count>0?p_route->http_uri_paths[0].http_uri_path:"") , (p_route->http_uri_paths_count>0?",...":"") , p_route->pfuncRestServiceEntry )
				nret = p_route->pfuncRestServiceEntry( ctx ) ;
				if( nret < 0 )
				{
					FATALLOG( "pfuncRestServiceEntry return [%d]" , nret )
					exit(9);
				}
				else if( nret > 0 )
				{
					ERRORLOG( "pfuncRestServiceEntry return [%d]" , nret )
					return RESTSERVER_ERROR_RESTSERVICE_ENTRY_RETURN;
				}
				else
				{
					DEBUGLOG( "pfuncRestServiceEntry return [%d]" , nret )
					return 0;
				}
			}
		}
	}
	
	DEBUGLOG( "leave RSAPIDispatchRestServiceControler" )
	
	return RESTSERVER_ERROR_NO_ROUTE_IN_CONFIG;
}

void DestroyRestServiceControler( struct RestServiceControler *controler )
{
	struct RestServiceRoute	*p_route = NULL ;
	struct RestServiceRoute	*p_next_rut = NULL ;
	
	DEBUGLOG( "enter RSAPIDestroyRestServiceControler" )
	
	list_for_each_entry_safe( p_route , p_next_rut , & (controler->routes_list) , struct RestServiceRoute , route_list_node )
	{
		INFOLOG( "delete a route[%.*s][%d][%.*s%s][%p] from rest service control" , p_route->http_method_len,p_route->http_method , p_route->http_uri_paths_count , (p_route->http_uri_paths_count>0?p_route->http_uri_paths[0].http_uri_path_len:0),(p_route->http_uri_paths_count>0?p_route->http_uri_paths[0].http_uri_path:"") , (p_route->http_uri_paths_count>0?",...":"") , p_route->pfuncRestServiceEntry )
		list_del( & (p_route->route_list_node) );
		free( p_route );
	}
	
	free( controler );
	
	DEBUGLOG( "leave RSAPIDestroyRestServiceControler" )
	
	return;
}

